#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include "tme_log.h"
#include "tme_utils.h"

static int32_t getTimeDiffUs()
{
    static uint32_t tickUs1 = 0;
    int32_t ret = -1;
    uint32_t tickUs2 = tme_utils_getTickUs();
    if (tickUs2 > tickUs1) {
        ret = tickUs2 - tickUs1;
    }
    tickUs1 = tickUs2;
    return ret;
}

static int32_t getRandom()
{
    static int32_t isInit = 0;
    if (!isInit) {
        isInit = 1;
        srand(tme_utils_getTickUs());
    }
    return rand();
}

#if 1 /* ----- api test ----- */

#include "tme_api.h"

#define TEST_FILE "./out.img"

void LoopReadAttr(void* handle, const char* attrName)
{
    char block[128+1] = {0};
    const int32_t blockSize = 128;
    int32_t ret = blockSize;

    int32_t attrSize = tme_attrSizeOf(handle, tme_attrGetId(handle, attrName));
    // TME_INFO("LoopReadAttr: attr:%s size:%d", attrName, attrSize);
    
    int32_t offset = 0;
    for (int32_t i = 0; ret == blockSize; i++)
    {
        memset(block, 0, sizeof(block));
        ret = tme_attrReadByName(handle, attrName, block, blockSize, &offset);
        TME_INFO("    part:%d, size:%d, data:%s", i, ret, block);
    }
}

void LoopAllAttr(void* handle)
{
    char attrName[1024] = {0};
    int32_t attrCount = 0;
    int32_t attrId = 0;
    while ((attrId = tme_attrGetIdLoop(handle, attrCount)) > 0)
    {
        if (tme_attrGetFullName(handle, attrId, attrName, sizeof(attrName)) != 0) {
            break;
        }
        TME_INFO("LoopAllAttr: %dth fullName:%s", attrCount, attrName);
        LoopReadAttr(handle, attrName);
        attrCount += 1;
    }
}

void LoopTimeFat(void* handle)
{
    ;
}

int main(void)
{
    void* handle = NULL;
    int32_t ret = 0;
    do
    {
        TME_ERR("-----> time: %d - skip", getTimeDiffUs());
        TME_ERR("-----> time: %d - test 0us", getTimeDiffUs());

        tme_utils_delayms(1);

        TME_ERR("-----> time: %d - test 1000us", getTimeDiffUs());

        handle = tme_open(TEST_FILE);
        // handle = tme_openReadOnly(TEST_FILE);
        if (!handle) {
            TME_ERR("open %s failed", TEST_FILE);
            break;
        }

        TME_ERR("-----> time: %d - tme_open", getTimeDiffUs());

        /* 写ShuXin */
        if (!tme_isReadOnly(handle))
        {
            /* ShuXin1: 添加ShuXin、通过id写 */
            const char* attr1 = "ShuXin1";
            int32_t id1 = tme_attrAdd(handle, attr1, 0);

            TME_ERR("-----> time: %d - tme_attrAdd", getTimeDiffUs());

            const char* value1 = "1234567890";
            ret = tme_attrWrite(handle, id1, (const void*)value1, strlen(value1));
            TME_INFO("tme_attrWrite: ret/%d id:0x%08X name:%s", ret, id1, attr1);

            TME_ERR("-----> time: %d - tme_attrWrite", getTimeDiffUs());

            /* ShuXin2: 通过父ShuXin添加、获取全名、通过全名写 */
            const char* attr2FullName = "ShuXin1:ShuXin2";
            int32_t id2 = tme_attrAdd(handle, attr2FullName, 0);

            TME_ERR("-----> time: %d - tme_attrAdd", getTimeDiffUs());

            const char* value2 = "qwertyuiop";
            ret = tme_attrWriteByName(handle, attr2FullName, (const void*)value2, strlen(value2));
            TME_INFO("tme_attrWrite: ret/%d id:0x%08X name:%s", ret, id2, attr2FullName);

            TME_ERR("-----> time: %d - tme_attrWriteByName", getTimeDiffUs());

            /* ShuXin3: 直接通过全名创建+写 */
            const char* attr3FullName = "ShuXin1:ShuXin2:ShuXin3";
            const char* value3 = "asdfghjkl";
            ret = tme_attrWriteByName(handle, attr3FullName, (const void*)value3, strlen(value3));

            TME_ERR("-----> time: %d - tme_attrWriteByName", getTimeDiffUs());

            int32_t id3 = tme_attrGetId(handle, attr3FullName);
            TME_INFO("tme_attrWrite: ret/%d id:0x%08X name:%s", ret, id3, attr3FullName);

            TME_ERR("-----> time: %d - tme_attrGetId", getTimeDiffUs());
        }

        /* 读ShuXin */
        {

            /* 打印文件信息 */
            char buff[1024] = {0};
            tme_info(handle, buff, sizeof(buff));
            TME_INFO("\r\n\r\n%s", buff);

            /* 遍历所有属性 */
            LoopAllAttr(handle);

            TME_ERR("-----> time: %d - LoopAllAttr", getTimeDiffUs());

            /* 遍历时间总览表 */
            LoopTimeFat(handle);

            TME_ERR("-----> time: %d - LoopTimeFat", getTimeDiffUs());
        }

    } while(0);

    if (handle) {
        tme_close(handle);
    }

    TME_ERR("-----> time: %d - tme_close", getTimeDiffUs());
    return 0;
}

#elif 1 /* ----- file & fat test ----- */

#include "tme_file_fmt.h"

#define TEST_FILE "./out.img"

int main(void)
{
    TME_File* fp = NULL;
    TME_FatChain* fc = NULL;
    do
    {
        /* file open */
        fp = tme_file_open(TEST_FILE, 'w');
        if (!fp) {
            TME_ERR("tme_file_open failed");
            break;
        }

        /* 写数据 */
        if (!fp->isReadOnly)
        {
            /* fat chain open */
            int32_t fatIndex = fp->head->fatIdleIndex;
            fc = tme_fat_chain_open(fp, fatIndex, TME_FAT_UNIT_TYPE_DATA);
            if (!fc) {
                TME_ERR("tme_fat_chain_open failed, fatIndex:%d", fatIndex);
                break;
            }
            TME_INFO("fat chain write begin ... fatIndex:%d", fc->fatWriteIndex);
            /* fat write */
            uint8_t tmpBuffer[512] = {0};
            int32_t targetSize = getRandom() % 65536;
            int32_t targetSizePart = getRandom() % 512;
            for (int32_t i = 0; i < targetSize; i += targetSizePart)
            {
                TME_INFO("fat chain writting ... fatIndex:%d->%d offset:%d totalSize:%d writeSize:%d",
                    fatIndex, fc->fatWriteIndex, fc->writeBlockOffset, fc->totalSize, i);
                memset(tmpBuffer, (i + 1) & 0xFF, sizeof(tmpBuffer));

                if (tme_fat_chain_write(fc, tmpBuffer, targetSizePart) != targetSizePart) {
                    TME_ERR("fat chain write failed");
                    break;
                }
            }
            TME_INFO("fat chain write end ...");
            /* fat chain close */
            tme_fat_chain_close(fc);
            fc = NULL;
        }
        
        /* 读数据,遍历所有fat链表 */
        if (0)
        {
            int32_t index1 = 0;
            int32_t index2 = 0;
            while (index1 < fp->head->fatCount && fp->fat[index1][index2])
            {
                /* 寻找链表起始 */
                if (GET_FAT_UNIT_BEGIN(fp->fat[index1][index2]))
                {
                    /* fat chain open */
                    int32_t fatIndex = index1 * fp->head->fatUnitNumber + index2;
                    fc = tme_fat_chain_open(fp, fatIndex, TME_FAT_UNIT_TYPE_DATA);
                    if (!fc) {
                        TME_ERR("tme_fat_chain_open failed, fatIndex:%d", fatIndex);
                        break;
                    }
                    TME_INFO("fat chain read begin ... fileSize:%d fatIndex:%d type:%d", fc->totalSize, fc->fatReadIndex, fc->type);
                    /* fat read */
                    uint8_t tmpValue;
                    for (int32_t i = 0; 1; i++)
                    {
                        if (tme_fat_chain_read(fc, &tmpValue, 1) != 1) {
                            TME_INFO("fat chain read end ...");
                            break;
                        }
                        if (i % FAT_UNIT_BLOCK_SIZE == 0) {
                            TME_INFO("fat chain reading ... fatIndex:%d->%d readSize:%d value:0x%02X",
                                fatIndex, fc->fatReadIndex, i, tmpValue);
                        }
                    }
                    /* fat chain close */
                    tme_fat_chain_close(fc);
                    fc = NULL;
                }
                /* next */
                index2 += 1;
                if (index2 >= fp->head->fatUnitNumber) {
                    index2 = 0;
                    index1 += 1;
                }
            }
        }

        TME_INFO("\r\n"
            "    file: %s \r\n"
            "    fileSize: %d \r\n"
            "    beginTime: %04d.%02d.%02d %02d:%02d:%02d.%03d \r\n"
            "    diffTime: %02d:%02d:%02d.%03d \r\n"
            "    durationTime: %02d:%02d:%02d.%03d \r\n"
            "    fatUnitOnFat: %d \r\n"
            "    fatUnitToBlockSize: %d \r\n"
            "    fatUnitOnUse: %d \r\n"
            "    fatTabSize: %d \r\n"
            "    fatTabOnUse: %d \r\n"
            "    isReadOnly: %d \r\n"
            "    isExtend: %d \r\n",
            TEST_FILE,
            fp->head->fileSize,
            fp->head->beginYear,
            fp->head->beginMonth,
            fp->head->beginDay,
            fp->head->beginMs / 1000 / 60 / 60,
            fp->head->beginMs / 1000 / 60 % 60,
            fp->head->beginMs / 1000 % 60,
            fp->head->beginMs % 1000,
            fp->diffTickMs / 1000 / 60 / 60,
            fp->diffTickMs / 1000 / 60 % 60,
            fp->diffTickMs / 1000 % 60,
            fp->diffTickMs % 1000,
            fp->head->durationMs / 1000 / 60 / 60,
            fp->head->durationMs / 1000 / 60 % 60,
            fp->head->durationMs / 1000 % 60,
            fp->head->durationMs % 1000,
            fp->head->fatUnitNumber,
            fp->head->fatUnitBlockSize,
            fp->head->fatIdleIndex,
            fp->head->fatSize,
            fp->head->fatCount,
            fp->isReadOnly,
            fp->isExtend);

    } while(0);

    if (fc) {
        tme_fat_chain_close(fc);
    }
    if (fp) {
        tme_file_close(fp);
    }
    return 0;
}

#elif 1 /* ----- time test ----- */

#include "tme_utils.h"

int main()
{
    uint16_t year = 0;
    uint8_t month = 0;
    uint8_t day = 0;
    uint32_t sec = 0;
    uint32_t ms = 0;

    TME_HEX("1234567890asdfghjklz", 20);

    while (1)
    {
        tme_utils_getTime(&year, &month, &day, &sec);
        ms = tme_utils_getTickMs();
        TME_INFO("%04d-%02d-%02d %02d:%02d:%02d.%03d",
            year, month, day, sec/3600, sec%3600/60, sec%60, ms%1000);
        tme_utils_delayms(1000);
    }

    return 0;
}

#elif 0 /* ----- mmap test ----- */

#include "tme_mmap.h"

#define TEST_FILE "./out.tme"
#define TEST_BLOCK_SIZE 4096

int main()
{
    /* 文件句柄 */
    FILE* fp = NULL;
    /* 先读写打开,不行再+创建打开 */
    fp = fopen(TEST_FILE, "rb+");
    if (!fp) {
        fp = fopen(TEST_FILE, "wb+");
        if (!fp) {
            TME_ERR("fopen fp %s failed", TEST_FILE);
            return -1;
        }
    }

    /* 同时映射3个内存位置操作 */
    void* wMem1 = NULL;
    void* wMem2 = NULL;
    void* rMem3 = NULL;
    uint8_t beginValue = 0xA0;
    uint8_t beginValue2 = 0xB0;
    for (int32_t i = 0; i < 10; i += 2)
    {
        /* create block 1 */
        wMem1 = tme_mmap_create(fp, TEST_BLOCK_SIZE, TEST_BLOCK_SIZE * i, 0);
        if (!wMem1) {
            TME_ERR("tme_mmap_create wMem1 size:%d offset:%d failed", TEST_BLOCK_SIZE, TEST_BLOCK_SIZE * i);
            break;
        }
        /* create block 2 */
        wMem2 = tme_mmap_create(fp, TEST_BLOCK_SIZE, TEST_BLOCK_SIZE * (i + 1), 0);
        if (!wMem2) {
            TME_ERR("tme_mmap_create wMem2 size:%d offset:%d failed", TEST_BLOCK_SIZE, TEST_BLOCK_SIZE * (i + 1));
            break;
        }
        /* create block 3 (横跨两块内存) */
        rMem3 = tme_mmap_create(fp, TEST_BLOCK_SIZE, TEST_BLOCK_SIZE * (i + 0.5), 1);
        if (!rMem3) {
            TME_ERR("tme_mmap_create rMem3 size:%d offset:%d failed", TEST_BLOCK_SIZE, TEST_BLOCK_SIZE * (i + 0.5));
            break;
        }

        /* write block 1 */
        memset(wMem1, beginValue++, TEST_BLOCK_SIZE);
        TME_INFO("-----> mmap 1 sync finash: size:%d offset:%d", TEST_BLOCK_SIZE, TEST_BLOCK_SIZE * i);
        /* write block 2 */
        memset(wMem2, beginValue2++, TEST_BLOCK_SIZE);
        TME_INFO("-----> mmap 2 sync finash: size:%d offset:%d", TEST_BLOCK_SIZE, TEST_BLOCK_SIZE * (i + 1));
        /* read block 3 (读取一头一尾数据) */
        TME_INFO("-----> mmap 3 read value: [0]:%02X [N]:%02X", ((uint8_t*)rMem3)[0], ((uint8_t*)rMem3)[TEST_BLOCK_SIZE - 1]);
        /* 对只读内存进行写操作(会崩溃) */
        // memset(rMem3, 0xFF, TEST_BLOCK_SIZE);

        /* release block 1 */
        tme_mmap_release(wMem1);
        /* release block 2 */
        tme_mmap_release(wMem2);
        /* release block 3 */
        tme_mmap_release(rMem3);
    }

    fclose(fp);
    return 0;
}

#endif
